package com.cloudeasy.user.service.expand;

import com.cloudeasy.api.enums.ResultCodeEnum;
import com.cloudeasy.api.exception.CloudEasyException;
import com.cloudeasy.api.vo.Result;
import com.cloudeasy.user.client.AreaClient;
import com.cloudeasy.user.constant.UserRoleConstant;
import com.cloudeasy.user.po.Permission;
import com.cloudeasy.user.po.Role;
import com.cloudeasy.user.po.RolePermission;
import com.cloudeasy.user.po.UserRole;
import com.cloudeasy.user.service.PermissionService;
import com.cloudeasy.user.service.RolePermissionService;
import com.cloudeasy.user.service.RoleService;
import com.cloudeasy.user.service.UserRoleService;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 权限校验服务
 */
@Service
public class VerifyPermissionService {
    private final UserRoleService userRoleService;

    private final RoleService roleService;

    private final RolePermissionService rolePermissionService;

    private final PermissionService permissionService;

    private final AreaClient areaClient;

    public VerifyPermissionService(UserRoleService userRoleService, RoleService roleService, RolePermissionService rolePermissionService, PermissionService permissionService, AreaClient areaClient) {
        this.userRoleService = userRoleService;
        this.roleService = roleService;
        this.rolePermissionService = rolePermissionService;
        this.permissionService = permissionService;
        this.areaClient = areaClient;
    }

    /**
     * 判断用户对该权限是否可以授权
     *
     * @param permissionId  权限Id
     * @param operateUserId 操作用户Id
     * @return {@link Boolean}
     */
    public Boolean canAuthorize(String permissionId, String operateUserId) {
        List<String> roleIds = getPossessPermissionRolesByUserId(permissionId, operateUserId).stream().map(UserRole::getRoleId).collect(Collectors.toList());
        return rolePermissionService.canAuthorize(roleIds, permissionId);
    }

    /**
     * 判断是否有权限对用户角色执行操作
     *
     * @param permissionId   执行权限Id
     * @param targetUserRole 目标用户角色记录
     * @param operateUserId  操作用户Id
     * @return {@link Boolean}
     */
    public Boolean canDoByUser(String permissionId, UserRole targetUserRole, String operateUserId) {
        List<UserRole> userRoles = getPossessPermissionRolesByUserId(permissionId, operateUserId);
        // 过滤掉相同区域
        for (UserRole userRole : userRoles) {
            // 如果在同一区域
            if (targetUserRole.getOperateArea().equals(userRole.getOperateArea())) {
                // 执行者为普通等级，没有权限执行
                if (userRole.getLevel().equals(UserRoleConstant.LEVEL_COMMON)) {
                    return false;
                }
                // 被执行者为普通等级，直接执行
                if (targetUserRole.getLevel().equals(UserRoleConstant.LEVEL_COMMON)) {
                    return true;
                }
                // 如果等级高于目标，可以执行
                if (userRole.getLevel() < targetUserRole.getLevel()) {
                    return true;
                }
                return false;
            }
        }
        List<String> parentPaths = userRoles.stream().map(UserRole::getOperateArea).distinct().collect(Collectors.toList());
        if (parentPaths.size() == 0) {
            return false;
        }
        return canDo(parentPaths, Collections.singletonList(targetUserRole.getOperateArea()));
    }

    /**
     * 判断是否有权限对区域执行操作
     *
     * @param permissionId  执行权限Id
     * @param targetAreas   目标区域列表
     * @param operateUserId 操作角色Id
     * @return {@link Boolean}
     */
    public Boolean canDo(String permissionId, List<String> targetAreas, String operateUserId) {
        List<UserRole> userRoles = getPossessPermissionRolesByUserId(permissionId, operateUserId);
        List<String> parentPaths = new ArrayList<>();
        // 过滤掉相同区域
        for (UserRole userRole : userRoles) {
            // 如果可以管理自己的区域
            if (userRole.getWithSelf()) {
                // 表示可以进行操作，去除掉这些目标区域
                if (targetAreas.size() == 1 && targetAreas.contains(userRole.getOperateArea())) {
                    return true;
                }
                targetAreas.remove(userRole.getOperateArea());
                parentPaths.add(userRole.getOperateArea());
            } else {
                if (!targetAreas.contains(userRole.getOperateArea())) {
                    parentPaths.add(userRole.getOperateArea());
                }
            }
        }
        if (targetAreas.size() == 0) {
            return true;
        }
        if (parentPaths.size() == 0) {
            return false;
        }
        return canDo(parentPaths, targetAreas);
    }

    /**
     * 执行最后的验证操作
     *
     * @param parentPaths 父路径列表
     * @param targetAreas 目标路径列表
     * @return {@link Boolean}
     */
    private Boolean canDo(List<String> parentPaths, List<String> targetAreas) {
        Result<Boolean> result = areaClient.isSubArea(parentPaths, targetAreas);
        if (!result.getCode().equals(ResultCodeEnum.SUCCESS.getCode())) {
            throw new CloudEasyException(ResultCodeEnum.REMOTE_ERROR);
        }
        return result.getData();
    }

    /**
     * 获取具有当前权限的用户管理区域
     *
     * @param permissionId  权限Id
     * @param operateUserId 操作用户Id
     * @return {@link List<String>}区域列表
     */
    public List<UserRole> getPossessPermissionRolesByUserId(String permissionId, String operateUserId) {
        // 能够访问到这里，用户一定是有权限的，只是权限操作区域问题，那么这些关联的信息一定不会为空
        // 获取具有相同作用的权限Id列表
        List<String> permissionIds = permissionService.findSamePermissions(permissionId)
                .stream().map(Permission::getId).collect(Collectors.toList());
        // 获取具有这些权限Id的可用角色Id
        List<String> roleIds = rolePermissionService.listByPermissionIds(permissionIds)
                .stream().map(RolePermission::getRoleId).collect(Collectors.toList());
        // 过滤掉禁用角色
        roleIds = roleService.listByRoleIdsAndEnable(roleIds, true)
                .stream().map(Role::getId).collect(Collectors.toList());
        // 获取用户具有角色的记录并过滤出区域Id
        return userRoleService.listByUserIdAndRoleIds(operateUserId, roleIds);
    }
}
